// filesys.cc 
//	Routines to manage the overall operation of the file system.
//	Implements routines to map from textual file names to files.
//
//	Each file in the file system has:
//	   A file header, stored in a sector on disk 
//		(the size of the file header data structure is arranged
//		to be precisely the size of 1 disk sector)
//	   A number of data blocks
//	   An entry in the file system directory
//
// 	The file system consists of several data structures:
//	   A bitmap of free disk sectors (cf. bitmap.h)
//	   A directory of file names and file headers
//
//      Both the bitmap and the directory are represented as normal
//	files.  Their file headers are located in specific sectors
//	(sector 0 and sector 1), so that the file system can find them 
//	on bootup.
//
//	The file system assumes that the bitmap and directory files are
//	kept "open" continuously while Nachos is running.
//
//	For those operations (such as Create, Remove) that modify the
//	directory and/or bitmap, if the operation succeeds, the changes
//	are written immediately back to disk (the two files are kept
//	open during all this time).  If the operation fails, and we have
//	modified part of the directory and/or bitmap, we simply discard
//	the changed version, without writing it back to disk.
//
// 	Our implementation at this point has the following restrictions:
//
//	   there is no synchronization for concurrent accesses
//	   files have a fixed size, set when the file is created
//	   files cannot be bigger than about 3KB in size
//	   there is no hierarchical directory structure, and only a limited
//	     number of files can be added to the system
//	   there is no attempt to make the system robust to failures
//	    (if Nachos exits in the middle of an operation that modifies
//	    the file system, it may corrupt the disk)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.
#ifndef FILESYS_STUB

#include "copyright.h"
#include "debug.h"
#include "disk.h"
#include "pbitmap.h"
#include "filehdr.h"
#include "filesys.h"
#include <string>

// Sectors containing the file headers for the bitmap of free sectors,
// and the directory of files.  These file headers are placed in well-known 
// sectors, so that they can be located on boot-up.
#define FreeMapSector 		0
#define DirectorySector 	1

// Initial file sizes for the bitmap and directory; until the file system
// supports extensible files, the directory size sets the maximum number 
// of files that can be loaded onto the disk.
#define FreeMapFileSize 	(NumSectors / BitsInByte)
#define NumDirEntries 		10
#define DirectoryFileSize 	(sizeof(DirectoryEntry) * NumDirEntries + 3*sizeof(int))

//----------------------------------------------------------------------
// FileSystem::FileSystem
// 	Initialize the file system.  If format = TRUE, the disk has
//	nothing on it, and we need to initialize the disk to contain
//	an empty directory, and a bitmap of free sectors (with almost but
//	not all of the sectors marked as free).  
//
//	If format = FALSE, we just have to open the files
//	representing the bitmap and the directory.
//
//	"format" -- should we initialize the disk?
//----------------------------------------------------------------------

FileSystem::FileSystem(bool format) {
	DEBUG(dbgFile, "Initializing the file system.");
	if (format) {
		PersistentBitmap *freeMap = new PersistentBitmap(NumSectors);
		rootDir = new Directory(NumDirEntries);
		rootDir->setSelfDir(DirectorySector);
		rootDir->setParentDir(DirectorySector);
		FileHeader *mapHdr = new FileHeader;
		FileHeader *dirHdr = new FileHeader;

		DEBUG(dbgFile, "Formatting the file system.");

		// First, allocate space for FileHeaders for the directory and bitmap
		// (make sure no one else grabs these!)
		freeMap->Mark(FreeMapSector);
		freeMap->Mark(DirectorySector);

		// Second, allocate space for the data blocks containing the contents
		// of the directory and bitmap files.  There better be enough space!

		ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize));
		ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize));

		// Flush the bitmap and directory FileHeaders back to disk
		// We need to do this before we can "Open" the file, since open
		// reads the file header off of disk (and currently the disk has garbage
		// on it!).

		DEBUG(dbgFile, "Writing headers back to disk.");
		mapHdr->WriteBack(FreeMapSector);
		dirHdr->WriteBack(DirectorySector);

		// OK to open the bitmap and directory files now
		// The file system operations assume these two files are left open
		// while Nachos is running.

		freeMapFile = new OpenFile(FreeMapSector);
		rootDirSector = DirectorySector;
		OpenFile* directoryFile = new OpenFile(rootDirSector);

		// Once we have the files "open", we can write the initial version
		// of each file back to disk.  The directory at this point is completely
		// empty; but the bitmap has been changed to reflect the fact that
		// sectors on the disk have been allocated for the file headers and
		// to hold the file data for the directory and bitmap.

		DEBUG(dbgFile, "Writing bitmap and directory back to disk.");
		freeMap->WriteBack(freeMapFile); // flush changes to disk
		rootDir->WriteBack(directoryFile);

		curDir = rootDir;

		if (debug->IsEnabled('f')) {
			freeMap->Print();
			rootDir->Print();
		}
		delete freeMap;
		delete mapHdr;
		delete dirHdr;
	} else {
		// if we are not formatting the disk, just open the files representing
		// the bitmap and directory; these are left open while Nachos is running
		freeMapFile = new OpenFile(FreeMapSector);
		rootDirSector = DirectorySector;
		OpenFile *directoryFile = new OpenFile(rootDirSector);
		int tsize = 0;
		directoryFile->ReadAt((char *)&tsize, sizeof(int), 0);
		rootDir = new Directory(tsize);
		rootDir->FetchFrom(directoryFile);
		curDir = rootDir;
	}
}

FileSystem::~FileSystem() {
	delete freeMapFile;
	delete rootDir;
	if( curDir != rootDir)
		delete curDir;
}


/*************************************************************************
 * Operations for the file part
 *
 * **********************************************************************
 */
//-----------------------------------------------------------------------------------
//Create a file with name given
//-----------------------------------------------------------------------------------
bool FileSystem::CreateFile(char *name, int initialSize) {
	return Create(name, initialSize, false);
}
//-----------------------------------------------------------------------------------
//Remove a file with name given
//-----------------------------------------------------------------------------------
bool FileSystem::RemoveFile(char *name) {
	return Remove(name, false);
}
//-----------------------------------------------------------------------------------
//Open a file with name given
//-----------------------------------------------------------------------------------
OpenFile* FileSystem::OpenAFile(char *name) // Open a file (UNIX open)
{
	return Open(name, false);
}
//----------------------------------------
//Thank create was here Rename a file
//----------------------------------------
void FileSystem::Rename(char *sour, char* dest)
{
	curDir->Rename(sour,dest);
	OpenFile *dirFile = new OpenFile(curDir->getSelfDir());
	curDir->WriteBack(dirFile);
	delete dirFile;
}

//----------------------------------------
//Thank create was here
//----------------------------------------
void FileSystem::Cat(char *name)
{
	FileHeader *fileHdr;
	int sector;
	sector = curDir->FindFile(name);
	if(sector == -1){
		cout<<"No such directory found !"<<endl;
		return;
	}
	fileHdr = new FileHeader;
	fileHdr->FetchFrom(sector);
	fileHdr->Print();
	delete fileHdr;
}

Directory* FileSystem::GetDirectory(char *name)
{
	int sector = -1;
		string tmp = string(name);
		if (tmp == "/" || tmp == "") {
			return rootDir;
		}
		if (*name == '/') //Find dir in root dir
		{
			sector = rootDir->FindDir(name);
		} else {
			sector = curDir->FindDir(name);
		}
		if (sector == -1) {
//			cout << "No such directory !";
			return NULL;
		}
		OpenFile *file = new OpenFile(sector);
		int tsize = 0;
		file->ReadAt((char *)&tsize, sizeof(int), 0);
		Directory* rdir = new Directory(tsize);
		rdir->FetchFrom(file);
		delete file;
		return rdir;
}

bool FileSystem::Copy(char *from, char *to)
{
	string strfromdir = string(from);
	string strtodir = string(to);
	string strfromname;
	string strtoname;
	Directory *dirfrom;
	Directory *dirto;
	OpenFile* filefrom;
	OpenFile* fileto;
	Directory *oldCurDir = curDir;
	int filelen = 0;
	char *chfromname;
	char *chtoname;
	int nf = strfromdir.find_last_of("/");
	int nt = strtodir.find_last_of("/");
	if(nf == -1){
		dirfrom = rootDir;
		strfromname = strfromdir;
		chfromname = (char*)strfromname.c_str();
	}
	else{
		dirfrom = GetDirectory((char*)strfromdir.substr(0, nf).c_str());
		strfromname = strfromdir.substr(nf+1,strfromdir.length()-nf-1);
		chfromname = (char*)strfromname.c_str();
	}
	if(nt == -1){
		dirto = rootDir;
		strtoname = strtodir;
		chtoname = (char*)strtoname.c_str();
	}
	else{
		dirto = GetDirectory((char*)strtodir.substr(0, nt).c_str());
		strtoname = strtodir.substr(nt+1,strtodir.length()-nt-1);
		chtoname = (char*)strtoname.c_str();
	}
	if(dirfrom == NULL || dirto == NULL)
		return false;
	int sectorfrom = dirfrom->FindFile(chfromname);
	int sectorto = dirto->FindFile(chtoname);
	if(sectorfrom == -1 || sectorto != -1)
		return false;
	filefrom = new OpenFile(sectorfrom);
	filelen = filefrom->Length();
	curDir = dirto;
	if(!this->CreateFile(chtoname, filelen))
		return false;
	fileto = this->OpenAFile(chtoname);
	char *buffer = new char[filelen];
	filefrom->ReadAt(buffer, filelen, 0);
	fileto->WriteAt(buffer, filelen, 0);
	delete filefrom;
	delete fileto;
	curDir = oldCurDir;
	return true;
}

bool FileSystem::PrintSectors(char *name)
{
	int sector = curDir->FindFile(name);
	FileHeader *hdr = new FileHeader;
	if (sector != -1) {
		printf("\nDirectory Sectors Used:\n");
		cout<<"File Header Sector: "<<sector<<" "<<endl;
		hdr->FetchFrom(sector);
		hdr->PrintSectors();
		delete hdr;
		return true;
	}
	else {
		sector = curDir->FindDir(name);
		if(sector != -1){
			printf("\nDirectory Sectors Used:\n");
			cout<<"File Header Sector: "<<sector<<" "<<endl;
			hdr->FetchFrom(sector);
			hdr->PrintSectors();
			delete hdr;
			return true;
		}
	}
	delete hdr;
	return false;
}


/*************************************************************************
 * Operations for the directory part
 *
 * **********************************************************************
 */


//-----------------------------------------------------------------------------------
//Create a directory with name given
//-----------------------------------------------------------------------------------
bool FileSystem::CreateDir(char *name) {
	return Create(name, 0, true);
}
//-----------------------------------------------------------------------------------
//Open a directory with name given
//-----------------------------------------------------------------------------------
OpenFile* FileSystem::OpenDir(char *name) {
	return Open(name, true);
}
//-----------------------------------------------------------------------------------
//Enter the directory with name given
//-----------------------------------------------------------------------------------
bool FileSystem::EnterDir(char *name) {
	int sector = -1;
	string tmp = string(name);
	if (tmp == "/") {
		if (curDir != rootDir) {
			delete curDir;
			curDir = rootDir;
		}
		return true;
	}
	if (*name == '/') //Find dir in root dir
	{
		sector = rootDir->FindDir(name);
	} else {
		sector = curDir->FindDir(name);
	}
	if (sector == -1) {
		cout << "No such directory !";
		return false;
	}
	delete curDir;
	OpenFile *file = new OpenFile(sector);
	int tsize = 0;
	file->ReadAt((char *)&tsize, sizeof(int), 0);
	curDir = new Directory(tsize);
	curDir->FetchFrom(file);
	delete file;
	return true;
}
//-----------------------------------------------------------------------------------
//Get out from a directory to its parent folder
//-----------------------------------------------------------------------------------
bool FileSystem::GetOut() {
	int sector = curDir->getParentDir();
	if(sector == -1) return false;
	delete curDir;
	OpenFile *pdf = new OpenFile(sector);
	int tsize = 0;
	pdf->ReadAt((char *)&tsize, sizeof(int), 0);
	curDir = new Directory(tsize);
	curDir->FetchFrom(pdf);
	delete pdf;
	return true;
}

//-----------------------------------------------------------------------------------
//Remove a directory with name given
//-----------------------------------------------------------------------------------
bool FileSystem::RemoveDir(char *name) {
	return Remove(name, true);
}

//----------------------------------------------------------------------
// FileSystem::List
// 	List all the files in the file system directory.
//----------------------------------------------------------------------

void FileSystem::List() {

	curDir->List();
}
void FileSystem::PrintDirSectors(){
	curDir->PrintSectors();
}

/////////////////////////////////////////////////////
//////
//      Bottom  Functions supplying surport for other operations
/////
////////////////////////////////////////////////////
//----------------------------------------------------------------------
// FileSystem::Create
// 	Create a file in the Nachos file system (similar to UNIX create).
//	Since we can't increase the size of files dynamically, we have
//	to give Create the initial size of the file.
//
//	The steps to create a file are:
//	  Make sure the file doesn't already exist
//        Allocate a sector for the file header
// 	  Allocate space on disk for the data blocks for the file
//	  Add the name to the directory
//	  Store the new file header on disk
//	  Flush the changes to the bitmap and the directory back to disk
//
//	Return TRUE if everything goes ok, otherwise, return FALSE.
//
// 	Create fails if:
//   		file is already in directory
//	 	no free space for file header
//	 	no free entry for file in directory
//	 	no free space for data blocks for the file
//
// 	Note that this implementation assumes there is no concurrent access
//	to the file system!
//
//	"name" -- name of file to be created
//	"initialSize" -- size of file to be created
//----------------------------------------------------------------------

bool FileSystem::Create(char *name, int initialSize, bool isDir)//edit
{
	PersistentBitmap *freeMap;
	FileHeader *hdr;
	int sector;
	bool success;

	DEBUG(dbgFile, "Creating file " << name << " size " << initialSize);

	if (isDir) {  //Cretate directory
		if (curDir->FindDir(name) != -1) {
			success = FALSE; // file is already in directory
			cout << "Directory is already in directory" << endl;
		} else {
			freeMap = new PersistentBitmap(freeMapFile, NumSectors);
			sector = freeMap->FindAndSet(); // find a sector to hold the file header
			if (sector == -1) {
				success = FALSE; // no free block for file header
				cout << "no free block for file header" << endl;
			} else if (!curDir->AddDir(name, sector, freeMap, curDir->getSelfDir())) {//edit
				success = FALSE; // no space in directory
				cout << "no space in directory" << endl;
			} else {
				hdr = new FileHeader;
				if (!hdr->Allocate(freeMap, DirectoryFileSize)) {
					success = FALSE; // no space on disk for data
					cout << "no space on disk for data" << endl;
				} else {
					hdr->WriteBack(sector);
					OpenFile *dirfile = new OpenFile(sector);
					Directory *dir = new Directory(NumDirEntries);
					dir->setParentDir(curDir->getSelfDir());
					dir->setSelfDir(sector);
					dir->WriteBack(dirfile);
					delete dir;
					delete dirfile;
					dirfile = new OpenFile(curDir->getSelfDir());
					curDir->WriteBack(dirfile);
					delete dirfile;
					freeMap->WriteBack(freeMapFile);
				}
				delete hdr;
			}
			delete freeMap;
		}
	} else { //Cretate file
		if (curDir->FindFile(name) != -1) {
			success = FALSE; // file is already in directory
			cout << "File is already in directory" << endl;
		} else {
			freeMap = new PersistentBitmap(freeMapFile, NumSectors);
			sector = freeMap->FindAndSet(); // find a sector to hold the file header
			if (sector == -1) {
				success = FALSE; // no free block for file header
				cout << "no free block for file header" << endl;
			} else if (!curDir->AddFile(name, sector, freeMap)) {//edit
				success = FALSE; // no space in directory
				cout << "no space in directory" << endl;
			} else {
				hdr = new FileHeader;
				if (!hdr->Allocate(freeMap, initialSize)) {
					success = FALSE; // no space on disk for data
					cout << "no space on disk for data" << endl;
				} else {
					success = TRUE;
					// everthing worked, flush all changes back to disk
					hdr->WriteBack(sector);
					OpenFile *dirfile = new OpenFile(curDir->getSelfDir());
					curDir->WriteBack(dirfile);
					delete dirfile;
					freeMap->WriteBack(freeMapFile);
				}
				delete hdr;
			}
			delete freeMap;
		}
	}
	return success;
}



//----------------------------------------------------------------------
// FileSystem::Remove
// 	Delete a file from the file system.  This requires:
//	    Remove it from the directory
//	    Delete the space for its header
//	    Delete the space for its data blocks
//	    Write changes to directory, bitmap back to disk
//
//	Return TRUE if the file was deleted, FALSE if the file wasn't
//	in the file system.
//
//	"name" -- the text name of the file to be removed
//----------------------------------------------------------------------
bool FileSystem::Remove(char *name, bool isDir) {
	PersistentBitmap *freeMap;
	FileHeader *fileHdr;
	int sector;
	OpenFile *file;
	if (isDir) {
		sector = curDir->FindDir(name);
		if (sector == -1) {
//			cout << "No such directory !";
			return false;
		}
		file = new OpenFile(sector);
		int tsize = 0;
		file->ReadAt((char *)&tsize, sizeof(int), 0);
		Directory *dir = new Directory(tsize);
		dir->FetchFrom(file);
		delete file;
		if (!dir->isEmpty()) {
			cout << "The directory is not empty !" << endl;
			delete dir;
			return false;
		}
	} else {
		sector = curDir->FindFile(name);
		if (sector == -1) {
			return FALSE; // file not found
		}
	}
	fileHdr = new FileHeader;
	fileHdr->FetchFrom(sector);

	freeMap = new PersistentBitmap(freeMapFile, NumSectors);

	fileHdr->Deallocate(freeMap); // remove data blocks
	freeMap->Clear(sector); // remove header block
	if (isDir)
		curDir->RemoveDir(name, freeMap); //edit
	else
		curDir->RemoveFile(name, freeMap); //edit

	freeMap->WriteBack(freeMapFile); // flush to disk
	file = new OpenFile(sector);
	curDir->WriteBack(file); // flush to disk
	delete fileHdr;
	delete freeMap;
	return TRUE;
}


//----------------------------------------------------------------------
// FileSystem::Open
// 	Open a file for reading and writing.
//	To open a file:
//	  Find the location of the file's header, using the directory
//	  Bring the header into memory
//
//	"name" -- the text name of the file to be opened
//----------------------------------------------------------------------


OpenFile* FileSystem::Open(char *name, bool isDir) {
	OpenFile *openFile = NULL;
	int sector;

	DEBUG(dbgFile, "Opening file" << name);
	sector = curDir->Find(name, isDir);
	if (sector >= 0)
		openFile = new OpenFile(sector); // name was found in directory
	return openFile; // return NULL if not found
}


//----------------------------------------------------------------------
// FileSystem::Print
// 	Print everything about the file system:
//	  the contents of the bitmap
//	  the contents of the directory
//	  for each file in the directory,
//	      the contents of the file header
//	      the data in the file
//----------------------------------------------------------------------

void FileSystem::Print() {
	FileHeader *bitHdr = new FileHeader;
	FileHeader *dirHdr = new FileHeader;
	PersistentBitmap *freeMap = new PersistentBitmap(freeMapFile, NumSectors);

	OpenFile *directoryFile = new OpenFile(rootDirSector);
	int tsize = 0;
	directoryFile->ReadAt((char *)&tsize, sizeof(int), 0);
	Directory *directory = new Directory(tsize);

	printf("Bit map file header:\n");
	bitHdr->FetchFrom(FreeMapSector);
	bitHdr->Print();

	printf("Directory file header:\n");
	dirHdr->FetchFrom(DirectorySector);
	dirHdr->Print();

	freeMap->Print();

	directory->FetchFrom(directoryFile);
	directory->Print();

	delete bitHdr;
	delete dirHdr;
	delete freeMap;
	delete directory;
}
#endif // FILESYS_STUB
